package com.flow.framework.web.controller;

import com.flow.framework.common.constant.FrameworkCommonConstant;
import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.json.JsonObject;
import com.flow.framework.core.constant.FrameworkCoreConstant;
import com.flow.framework.core.response.Response;
import com.flow.framework.core.service.properties.ISystemConfigPropertiesService;
import com.flow.framework.core.system.listener.lifecycle.ISystemLifecycleListener;
import com.flow.framework.web.constant.FrameworkWebConstant;
import com.flow.framework.web.util.HttpServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.slf4j.MDC;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * 发生错误时处理的controller
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/12/18
 */
@Slf4j
@RestController
@RequestMapping("${error.path:" + FrameworkWebConstant.DEFAULT_ERROR_PATH + "}")
public class ErrorController extends AbstractErrorController implements ISystemLifecycleListener {

    private final ISystemConfigPropertiesService systemConfigPropertiesService;

    private final ErrorAttributes errorAttributes;

    private String errorPath;

    public ErrorController(ErrorAttributes errorAttributes, List<ErrorViewResolver> errorViewResolvers,
                           ISystemConfigPropertiesService systemConfigPropertiesService) {
        super(errorAttributes, errorViewResolvers);
        this.errorAttributes = errorAttributes;
        this.systemConfigPropertiesService = systemConfigPropertiesService;
    }

    @RequestMapping
    public Response<?> error(HttpServletRequest request, HttpServletResponse response) {
        // 如果找不到对应的RequestMapping，则filter会全部执行完，此时相应的上下文会全部被清空，spring会重新将请求forward到
        // 到处理错误的controller，所以这里需要重新设置环境变量
        String traceId = (String) request.getAttribute(FrameworkCommonConstant.GLOBAL_LOG_TRACE_KEY);
        MDC.put(FrameworkCommonConstant.GLOBAL_LOG_TRACE_KEY, traceId);
        String previousAppName = (String) request.getAttribute(FrameworkCoreConstant.GLOBAL_PREVIOUS_APP_KEY);
        MDC.put(FrameworkCoreConstant.GLOBAL_PREVIOUS_APP_KEY, previousAppName);

        WebRequest webRequest = new ServletWebRequest(request);
        Throwable error = errorAttributes.getError(webRequest);
        if (null != error) {
            log.error("request occur error. ", error);
        }

        response.setContentType(ContentType.APPLICATION_JSON.toString());
        Response<?> frameworkResponse;
        Object attribute = request.getAttribute(FrameworkCoreConstant.FRAMEWORK_RESPONSE_KEY);
        if (null != attribute) {
            frameworkResponse = (Response) attribute;
        } else {
            HttpStatus status = getStatus(request);
            log.error("request occur error. http code : {}", status);
            if (status == HttpStatus.NOT_FOUND) {
                frameworkResponse = Response.failed(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
            } else {
                frameworkResponse = Response.failed(SystemErrorCode.API_SERVER_ERROR);
            }
        }

        // 如果为内部请求，则不再响应body，而是将框架响应放入header响应给客户端
        boolean isRpcRequest = HttpServletUtil.isRpcRequest(request);
        if (isRpcRequest) {
            try {
                response.setHeader(FrameworkCoreConstant.FRAMEWORK_RESPONSE_KEY,
                        URLEncoder.encode(JsonObject.toString(frameworkResponse), StandardCharsets.UTF_8.name()));
            } catch (UnsupportedEncodingException e) {
                log.error("encoding error.", e);
            }
            // 出错之后返回null不会触发com.flow.framework.web.advice.WebResponseBodyAdvice
            return null;
        }
        return frameworkResponse;
    }

    @Override
    public String getErrorPath() {
        return errorPath;
    }

    @Override
    public void onStartUp() {
        this.errorPath = systemConfigPropertiesService.getConfigValue("server.error.path", FrameworkWebConstant.DEFAULT_ERROR_PATH);
    }
}